package me.zhengjie.rest;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.*;

import cn.hutool.core.util.IdUtil;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.annotation.Log;
import me.zhengjie.exception.BadRequestException;
import me.zhengjie.domain.ErpChunk;
import me.zhengjie.domain.ErpFiles;
import me.zhengjie.domain.vo.GooidServerSay;
import me.zhengjie.domain.vo.R;
import me.zhengjie.service.FzChunkService;
import me.zhengjie.service.FzFilesService;
import me.zhengjie.util.FileUtils;
import me.zhengjie.utils.SecurityUtils;
import me.zhengjie.utils.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


@Slf4j
@Controller
@RequiredArgsConstructor
@RequestMapping("/api/file_upload")
public class FileUploadController {
    @Value("${file.windows.path}")
    private String uploadFolder;

    private final FzChunkService chunkService;

    private final FzFilesService filesService;

    /**
     * 验证当前文件块是否上传
     *
     * @param chunk
     * @return
     */
    @RequestMapping(value = "/chunk", method = RequestMethod.GET)
    @ResponseBody
    public GooidServerSay checkChunks(ErpChunk chunk) {
        GooidServerSay ret = new GooidServerSay();
        try {
            String identifier = chunk.getIdentifier();
            List<ErpChunk> erpChunks = chunkService.findByIdentifier(identifier);
            Set<Integer> chunkNumbers = new HashSet<>();
            for (ErpChunk erpChunk : erpChunks) {
                chunkNumbers.add(erpChunk.getChunkNumber());

            }
            Map<String, Object> res = new HashMap<>();
            res.put("chunkNumbers", chunkNumbers);
            String md5 = identifier;
            List<ErpFiles> files = filesService.findByMd5(md5);
            if (files.size() > 0) {
                res.put("code", 200);
                res.put("fileName", files.get(0).getFileName());
                res.put("fileUrl", files.get(0).getFileUrl());
                res.put("fileSize", files.get(0).getFileSize());
            } else if (erpChunks.size() > 0 && erpChunks.size() == erpChunks.get(0).getTotalChunks()) {
                res.put("message", "上传成功！");
                res.put("code", 205);
            }
            ret.setResult(res);
        } catch (Exception e) {
            ret.setCode("error");
            ret.setMsg("操作失败详细信息:" + e.getMessage());
            e.printStackTrace();
        }
        return ret;
    }

    @RequestMapping(value = "/image/query", method = RequestMethod.GET)
    @ResponseBody
    public R<List<ErpFiles>> query(@RequestParam String bid) {
        if (StringUtils.isBlank(bid)) {
            return R.fail("参数不能为空");
        } else {
            List<ErpFiles> files = filesService.findByBid(bid);
            return R.data(files);
        }
    }

    /**
     * 分块上传
     *
     * @param chunk
     * @return
     */
    /**
     * @param chunk
     * @return
     */
    @RequestMapping(value = "/chunk", method = RequestMethod.POST)
    @ResponseBody
    public GooidServerSay saveChunk(ErpChunk chunk) {
        GooidServerSay ret = new GooidServerSay();
        try {
            // 这里的操作和保存单段落的基本是一致的~
            MultipartFile file = chunk.getFile();
            String identifier = chunk.getIdentifier();
            byte[] bytes;
            try {
                bytes = file.getBytes();
                // 这里的不同之处在于这里进行了一个保存分块时将文件名的按照-chunkNumber的进行保存
                Path path = Paths
                        .get(generatePath(uploadFolder + SecurityUtils.getCurrentUsername() + File.separator, chunk));
                Files.write(path, bytes);
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (chunk.getFilename().lastIndexOf(".") > 0) {
                chunk.setType(chunk.getFilename().substring(chunk.getFilename().lastIndexOf(".")));
            }
            chunk.setId(IdUtil.simpleUUID());
            chunkService.save(chunk);
            // 这里进行的是保存到redis，并返回集合的大小的操作
            int chunks = chunkService.findByIdentifier(identifier).size();
            Map<String, Object> result = new HashMap<>();
            // 如果集合的大小和totalChunks相等，判定分块已经上传完毕，进行merge操作
            if (chunks == chunk.getTotalChunks().intValue()) {
                result.put("message", "上传成功！");
                result.put("code", 205);
            }

            ret.setResult(result);
        } catch (Exception e) {
            e.printStackTrace();
            ret.setCode("error");
            ret.setMsg("操作失败详细信息:" + e.getMessage());
        }
        return ret;
    }

    /**
     * 生成分块的文件路径
     */
    private static String generatePath(String uploadFolder, ErpChunk chunk) {
        StringBuilder sb = new StringBuilder();
        // 拼接上传的路径
        sb.append(uploadFolder).append(File.separator).append(chunk.getIdentifier());
        // 判断uploadFolder/identifier 路径是否存在，不存在则创建
        if (!Files.isWritable(Paths.get(sb.toString()))) {
            try {
                Files.createDirectories(Paths.get(sb.toString()));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        // 返回以 - 隔离的分块文件，后面跟的chunkNumber方便后面进行排序进行merge
        return sb.append(File.separator).append(chunk.getFilename()).append("-").append(chunk.getChunkNumber())
                .toString();

    }

    @RequestMapping(value = "/merge", method = RequestMethod.POST)
    @ResponseBody
    public GooidServerSay mergeChunks(ErpChunk chunk) {
        GooidServerSay ret = new GooidServerSay();
        try {
            // 如果合并后的路径不存在，则新建
            int offset = chunk.getRelativePath().lastIndexOf("/");
            String longPath = uploadFolder + SecurityUtils.getCurrentUsername() + File.separator + chunk.getBId() + File.separator;
            if (offset != -1) {
                longPath = longPath + chunk.getRelativePath().substring(0, offset);
            }
            Path createdDir = Paths.get(longPath);
            if (!Files.isWritable(createdDir)) {
                Files.createDirectories(createdDir);
            }
            // 合并的文件名
            String target = uploadFolder + SecurityUtils.getCurrentUsername() + File.separator + chunk.getBId() + File.separator + chunk.getRelativePath();
            File file = new File(target);
            if (!file.exists()) {
                // 创建文件
                Files.createFile(Paths.get(target));
                // 遍历分块的文件夹，并进行过滤和排序后以追加的方式写入到合并后的文件
                Files.list(Paths.get(uploadFolder + SecurityUtils.getCurrentUsername() + File.separator
                        + chunk.getIdentifier()))
                        // 过滤带有"-"的文件
                        .filter(path -> path.getFileName().toString().contains("-"))
                        // 按照从小到大进行排序
                        .sorted((o1, o2) -> {
                            String p1 = o1.getFileName().toString();
                            String p2 = o2.getFileName().toString();
                            int i1 = p1.lastIndexOf("-");
                            int i2 = p2.lastIndexOf("-");
                            return Integer.valueOf(p2.substring(i2)).compareTo(Integer.valueOf(p1.substring(i1)));
                        }).forEach(path -> {
                    try {
                        // 以追加的形式写入文件
                        Files.write(Paths.get(target), Files.readAllBytes(path), StandardOpenOption.APPEND);
                        // 合并后删除该块
                        Files.delete(path);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                });
                File dir = new File(uploadFolder + SecurityUtils.getCurrentUsername() + File.separator
                        + chunk.getIdentifier());
                if (dir.exists()) {
                    dir.delete();
                }
            }


            ErpFiles files = new ErpFiles();
            files.setId(IdUtil.simpleUUID());
            files.setFileName(chunk.getFilename());
            files.setCreationTime(new Date());
            files.setFileMd5(chunk.getIdentifier());
            files.setFileSize(chunk.getTotalSize());
            files.setBId(chunk.getBId());
            files.setFType(chunk.getFType());
            if (chunk.getFilename().lastIndexOf(".") > 0) {
                files.setFileType(chunk.getFilename().substring(chunk.getFilename().lastIndexOf(".")).toLowerCase());
            }

            String[] args = chunk.getRelativePath().split("/");
            if (args.length > 1) {
                files.setRootDir(args[0]);
            }

            files.setFileUrl(chunk.getRelativePath());
            files.setUserName(SecurityUtils.getCurrentUsername());
            filesService.save(files);
            Map<String, String> result = new HashMap<String, String>();
            result.put("fileName", chunk.getFilename());
            result.put("fileUrl", files.getFileUrl());
            result.put("fileDir", files.getRootDir() == null ? "请输入样品名称" : files.getRootDir());
            result.put("fileSize", chunk.getTotalSize() + "");
            ret.setResult(result);
        } catch (IOException e) {
            e.printStackTrace();
            ret.setCode("error");
            ret.setMsg("操作失败详细信息:" + e.getMessage());
        }
        return ret;
    }

    //应用场景 打包
    @RequestMapping(value = "/downloadZip")
    public void downloadZip(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        String bid = req.getParameter("bid");
        if (StringUtils.isBlank(bid)) {
            throw new BadRequestException("参数不能为空！");
        }

        List<ErpFiles> files = filesService.findByBid(bid);
        if (files.isEmpty()){
            throw new BadRequestException("文件不存在!");
        }else {
            String zipPath = uploadFolder + SecurityUtils.getCurrentUsername();
            String sourcePath = zipPath + File.separator + bid;
            resp.setContentType("application/octet-stream");
            //如果输出的是中文名的文件，在此处就要用URLEncoder.encode方法进行处理
            resp.setHeader("Content-Disposition", "attachment;filename=file.zip");
            FileUtils.compressToZip(sourcePath, resp);
        }
    }

    @RequestMapping(value = "/deleteFile")
    @ResponseBody
    public R<String> deleteFile(HttpServletRequest req) {
        String bid = req.getParameter("bid");
        if (StringUtils.isBlank(bid)) {
            return R.fail("参数不能为空");
        } else {
            String sourcePath = uploadFolder + SecurityUtils.getCurrentUsername() + File.separator + bid;
            if (FileUtils.deleteDir(new File(sourcePath))) {
                filesService.deleteByBid(bid);
                chunkService.deleteByBid(bid);
                return R.success("删除成功!");
            } else {
                return R.fail("删除失败!");
            }
        }
    }
}
